import cv2  # 导入cv2模块,用于读取和处理图像
import numpy as np  # 导入numpy模块,主要用于数值计算
import os  # 导入os模块,用于处理文件和目录
from os.path import exists  # 从os.path模块导入exists函数,用于检测文件或目录是否存在
from imutils import paths  # 导入imutils中的paths工具,用于获取文件路径
import pickle  # 导入pickle模块,用于序列化和反序列化Python对象结构
from tqdm import tqdm  # 导入tqdm模块,用于在循环中添加进度条
from tensorflow.keras.preprocessing import image  # 从tensorflow.keras.preprocessing导入image工具,用于处理图像
import logging  # 用于记录日志
from PIL import Image

logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') # 配置logging

# 定义函数get_size,获取文件大小,返回文件大小的兆字节的浮点数表示
def get_size(file):
    """
    获取指定文件的大小（以MB为单位）
    
    参数:
    file (str): 文件的路径
    
    返回:
    float: 文件大小（MB）
    """
    if not os.path.exists(file):  # 检查文件是否存在
        logging.warning(f"文件 {file} 不存在。")
        return 0.0  # 如果文件不存在，返回0.0

    size_bytes = os.path.getsize(file)  # 获取文件大小（字节）
    size_mb = size_bytes / (1024 * 1024)  # 转换为MB
    return size_mb  # 返回文件大小（MB）

# 定义函数createXY,用于从图像创建特征(X)和标签(y)
def createXY(train_folder, dest_folder, batch_size=64):
    x_file_path = os.path.join(dest_folder, "X.pkl")  # 设置X文件的路径,用于保存特征数据
    y_file_path = os.path.join(dest_folder, "y.pkl")  # 设置y文件的路径,用于保存标签数据

    # 如果 X 和 y 已经存在，则直接读取，不再重新构建
    if os.path.exists(x_file_path) and os.path.exists(y_file_path):
        logging.info("X和y已经存在，直接读取")  # 提示用户X和y文件已经存在
        logging.info(f"X文件大小:{get_size(x_file_path):.2f}MB")  # 打印X文件的大小
        logging.info(f"y文件大小:{get_size(y_file_path):.2f}MB")  # 打印y文件的大小
        
        # 用 pickle 读取X和y文件
        X = pickle.load(open(x_file_path, "rb"))  # 读取X特征
        y = pickle.load(open(y_file_path, "rb"))  # 读取y标签
        return X, y  # 直接返回已存在的X和y
    
    logging.info("X,ybu存在，开始构建X和y")
    logging.info("读取所有图像，生成X和y")  # 提示用户开始读取图像并生成X和y
    image_paths = list(paths.list_images(train_folder))  # 获取训练文件夹中所有图像的路径

    X = []  # 初始化X列表,用于存储特征
    y = []  # 初始化y列表,用于存储标签
    
    

    # 计算需要的批次数
    num_batches = len(image_paths) // batch_size + (1 if len(image_paths) % batch_size else 0)

    # 使用进度条对批次进行循环处理
    for idx in tqdm(range(num_batches), desc="读取图像"):
        batch_images = []  # 初始化存储批次图像的列表
        batch_labels = []  # 初始化存储批次标签的列表
        
        start = idx * batch_size  # 计算批次开始的索引
        end = min((idx + 1) * batch_size, len(image_paths))  # 计算批次结束的索引

        # 对于每个批次中的图像
        for i in range(start, end):
            image_path = image_paths[i]  # 获取图像路径
            
            img = Image.open(image_path)  # 以PIL形式读取图像
            img = img.convert('L')
            img = np.array(img)
            img = cv2.resize(img, (32, 32))  # 调整图像大小到32x32
            if img is None:
                logging.warning(f"无法读取图像: {image_path}")
                return None
            batch_images.append(img)  # 将图像数组添加到批次图像列表
            
            label = image_path.split(os.path.sep)[-1].split('.')[0]  # 从文件名中解析出字符串标签
            label = 1 if label == 'dog' else 0  # 如果标签是'dog'则为1,否则为0
            batch_labels.extend([label])  # 将标签添加到批次标签列表

        batch_images = np.array(batch_images)  # 将批次图像列表转换为numpy数组
        
        batch_pixels = batch_images.reshape(batch_images.shape[0], -1)  # 将 batch_images 展平

        X.extend(batch_pixels)  # 将处理后的图像特征添加到X列表
        y.extend(batch_labels)  # 将标签添加到y列表

    logging.info(f"X.shape: {np.shape(X)}")  # 打印X的形状
    logging.info(f"y.shape: {np.shape(y)}")  # 打印y的形状
    
        
    # 用 pickle 保存X和y文件
    pickle.dump(X, open(x_file_path, "wb"))  # 保存X特征
    pickle.dump(y, open(y_file_path, "wb"))  # 保存y标签

    return X, y  # 返回构建的X和y

def format_table(data, headers):
    # 将数据转换为数组
    data_array = np.array(data)
    
    # 计算每一列数据中的小数的整数部分长度和小数部分长度
    int_lengths = np.array([len(str(int(item))) if isinstance(item, float) else len(str(item)) for item in data_array.flatten()])
    dec_lengths = np.array([len(str(item).split('.')[1]) if isinstance(item, float) else 0 for item in data_array.flatten()])
    
    # 计算每一列的列宽
    col_widths = np.maximum(int_lengths.max(axis=0) + dec_lengths.max(axis=0), np.array([len(header) for header in headers]))
    
    # 计算小数部分右边应增加的空格数
    dec_spaces = dec_lengths.max(axis=0) - dec_lengths
    
    # 格式化每一行
    formatted_rows = []
    for row in data_array:
        formatted_row = []
        for i, item in enumerate(row):
            if isinstance(item, float):
                # 计算整数部分应增加的空格数
                int_spaces = col_widths[i] - len(str(int(item))) - dec_lengths[i] - 1  # -1 for the decimal point
                formatted_item = f"{item:>{int_spaces}.{dec_lengths[i]}f}"
            else:
                formatted_item = str(item).ljust(col_widths[i])
            formatted_row.append(formatted_item)
        formatted_rows.append(' ' * 10 + ' '.join(formatted_row))
    
    # 添加表头
    formatted_rows.insert(0, ' ' * 10 + ' '.join(header.ljust(col_widths[i]) for i, header in enumerate(headers)))
    
    return '\n'.join(formatted_rows)
